package com.googlecode.continuity.plugin.codecomment.client;

import java.util.Iterator;
import java.util.List;
import java.util.Set;

import com.google.gwt.user.client.rpc.AsyncCallback;
import com.google.gwt.user.client.ui.*;
import com.googlecode.continuity.core.client.CoreGWT;
import com.googlecode.continuity.core.client.Reply;
import com.googlecode.continuity.plugin.gwtext.client.PromptTextArea;
import com.googlecode.continuity.plugin.gwtext.client.RatingControl;
import com.googlecode.continuity.plugin.gwtext.client.RatingListener;
import com.googlecode.continuity.plugin.gwtext.client.StarRating;
import com.googlecode.continuity.plugin.rating.client.RatingInfo;
import com.googlecode.continuity.plugin.rating.client.RatingRPCAsync;
import com.googlecode.continuity.plugin.svnbrowser.client.SVNRepositoryHandle;

class ShowCommentBox extends CommentBox {
// ------------------------------ FIELDS ------------------------------

  private VerticalPanel replyPanel;
  private ScrollPanel replyContainer;
  private PromptTextArea replyArea;
  private HorizontalPanel replyControl;
  private Label toggleMoreLabel;
  private FlexTable moreInfo;
  private HTML replyToggle;

  private boolean showingReplies;
  private boolean showingMoreInfo;

  private int simpleHeaderRow = 0;
  private int moreInfoRow = 1;
  private int messageRow = 2;
  private int relatedCommentRow = 3;
  private int ratingRow = 4;
  private int replyDisplayRow = 5;
  private int replyInfoRow = 6;
  private int replyAreaRow = 7;
  private int replyControlRow = 8;
  private Label addReply;

// --------------------------- CONSTRUCTORS ---------------------------

  public ShowCommentBox(SVNRepositoryHandle svnHandle, CodeComment comment, CodeCommentArea area) {
    super(svnHandle, comment, area);
    showingReplies = false;
    showingMoreInfo = false;

    setupHeader();
    setupMessage();
    setupRating();
    setupRelated();
    setupReplies();
  }

  protected void setupHeader() {
    // setup simple header
    HorizontalPanel simpleHeader = new HorizontalPanel();
    Label author = new Label(comment.getAuthor().getUsername());
    Label time = new Label(CoreGWT.getLocaleDateFormat().format(comment.getCreationDate()));
    toggleMoreLabel = new Label("(" + (showingMoreInfo ? messages.less() : messages.more()) + ")");

    toggleMoreLabel.addClickListener(new ClickListener() {
      public void onClick(Widget widget) {
        toggleMoreInfo();
      }
    });

    simpleHeader.add(author);
    simpleHeader.add(time);
    simpleHeader.add(toggleMoreLabel);

    simpleHeader.addStyleName("simpleHeader");
    author.addStyleName("author");
    time.addStyleName("time");
    toggleMoreLabel.addStyleName("toggleMoreInfo");
    toggleMoreLabel.addStyleName("link");
    simpleHeader.setCellHorizontalAlignment(time, HasHorizontalAlignment.ALIGN_RIGHT);
    simpleHeader.setCellWidth(time, "100%");

    content.setWidget(simpleHeaderRow, 0, simpleHeader);
  }

  private void toggleMoreInfo() {
    toggleMoreLabel.setText("(" + (showingMoreInfo ? messages.more() : messages.less()) + ")");
    if (moreInfo == null) {
      setupMoreInfo();
    }

    if (showingMoreInfo) {
      content.remove(moreInfo);
    } else {
      content.setWidget(moreInfoRow, 0, moreInfo);
    }

    showingMoreInfo = !showingMoreInfo;
  }

  private void setupMoreInfo() {
    moreInfo = new FlexTable();

    // show severity
    moreInfo.setWidget(0, 0, new Label(messages.type()));
    Label severity = new Label(comment.getSeverity().getName());
    moreInfo.setWidget(0, 1, severity);
    severity.addStyleName("severity-" + comment.getSeverity().getName());
    severity.addStyleName("value");

    // show author
    moreInfo.setWidget(1, 0, new Label(messages.author()));
    Label author = new Label(comment.getAuthor().getUsername());
    author.addStyleName("value");
    moreInfo.setWidget(1, 1, author);

    // show time
    moreInfo.setWidget(2, 0, new Label(messages.visibleTo()));
    Label visibility = new Label("all");
    visibility.addStyleName("value");
    moreInfo.setWidget(2, 1, visibility);

    // show time
    moreInfo.setWidget(3, 0, new Label(messages.createdOn()));
    Label time = new Label(CoreGWT.getLocaleDateFormat().format(comment.getCreationDate()));
    time.addStyleName("value");
    moreInfo.setWidget(3, 1, time);

    moreInfo.addStyleName("moreInfo");
  }

  protected void setupMessage() {
    Label message = new Label(comment.getContent());
    content.setWidget(messageRow, 0, message);
    content.getFlexCellFormatter().addStyleName(messageRow, 0, "message");
  }

  private void setupRating() {
    ((RatingRPCAsync) CoreGWT.getRPC("RatingRPCAsync")).getRating(comment.getId(), new AsyncCallback() {
      public void onFailure(Throwable throwable) {
        //todo: show error
      }

      public void onSuccess(Object o) {
        RatingInfo info = (RatingInfo) o;
        HorizontalPanel ratingContainer = new HorizontalPanel();

        final StarRating allRatings = new StarRating(info.getAverage(), false);
        final Label allRatingsCount = new Label(messages.ratingCount(info.getRatingCount()));

        ratingContainer.add(allRatings);
        ratingContainer.add(allRatingsCount);
        allRatingsCount.setWordWrap(false);

        if (!comment.getAuthor().equals(CoreGWT.getCurrentUser())) {

          final StarRating yourRating = new StarRating(info.getCurrentUserRating(), true);
          final Label yourRatingInfo = new Label(messages.yourRating());

          yourRating.addRatingListener(new RatingListener() {
            public void onRated(RatingControl source, float rating) {
              ((RatingRPCAsync) CoreGWT.getRPC("RatingRPCAsync"))
                  .setRating(comment.getId(), Math.round(rating), new AsyncCallback() {
                    public void onFailure(Throwable throwable) {
                      // todo: show error
                    }

                    public void onSuccess(Object o) {
                      RatingInfo info = (RatingInfo) o;
                      allRatings.setRating(info.getAverage());
                      allRatingsCount.setText(messages.ratingCount(info.getRatingCount()));
                      yourRating.setRating(info.getCurrentUserRating());
                      yourRatingInfo.setText(messages.ratingSaved());
                    }
                  });
            }
          });

          ratingContainer.add(yourRating);
          ratingContainer.add(yourRatingInfo);
          yourRatingInfo.setWordWrap(false);
          ratingContainer.setCellHorizontalAlignment(yourRating, HasHorizontalAlignment.ALIGN_RIGHT);
          ratingContainer.setCellWidth(yourRating, "100%");
        }

        ratingContainer.addStyleName("ratingRow");
        content.setWidget(ratingRow, 0, ratingContainer);
      }
    });
  }

  private void setupRelated() {
    if (comment.getRelatedComments().size() == 0) {
      return;
    }

    VerticalPanel relatedList = new VerticalPanel();

    Label relatedHeader = new Label(messages.relatedCommentsHeader());
    relatedHeader.addStyleName("header");
    relatedList.add(relatedHeader);

    // show related comment links
    Iterator it = comment.getRelatedComments().iterator();
    while (it.hasNext()) {
      CodeComment related = (CodeComment) it.next();
      CodePointer pointer = related.getLocation();
      Label relatedLabel;
      if (pointer.getFirstLine() == pointer.getLastLine()) {
        relatedLabel = new Label(messages.relatedCommentSingleLine(pointer.getName(), pointer.getFirstLine(),
            pointer.getVersion()));
      } else {
        relatedLabel = new Label(messages.relatedCommentMultipleLines(pointer.getName(), pointer.getFirstLine(),
            pointer.getLastLine(), pointer.getVersion()));
      }
      relatedLabel.addStyleName("severity-" + related.getSeverity().getName());
//        relatedLabel.addStyleName("link");
      relatedLabel.addStyleName("relatedListEntry");
      relatedList.add(relatedLabel);
    }

    relatedList.addStyleName("relatedList");

    content.setWidget(relatedCommentRow, 0, relatedList);
  }

  private void setupReplies() {
    // reply info
    HorizontalPanel replyInformation = new HorizontalPanel();

    replyToggle = new HTML();
    replyToggle.addClickListener(new ClickListener() {
      public void onClick(Widget widget) {
        toggleReplyDisplay();
      }
    });
    refreshReplyToggle();

    addReply = new Label(messages.reply());
    addReply.addClickListener(new ClickListener() {
      public void onClick(Widget widget) {
        composeReply();
      }
    });

    replyInformation.add(replyToggle);
    replyInformation.add(addReply);

    replyInformation.addStyleName("replyInformation");
    replyToggle.setWordWrap(false);
    addReply.addStyleName("link");
    replyInformation.setCellHorizontalAlignment(addReply, HasHorizontalAlignment.ALIGN_RIGHT);
    replyInformation.setCellWidth(addReply, "100%");

    content.setWidget(replyInfoRow, 0, replyInformation);
  }

  private void composeReply() {
    // composition area
    if (replyArea == null) {
      setupReplyArea();
    }
    content.getFlexCellFormatter().addStyleName(replyAreaRow, 0, "replyAreaContainer");
    content.setWidget(replyAreaRow, 0, replyArea);

    // composition controls
    if (replyControl == null) {
      setupReplyControl();
    }
    content.setWidget(replyControlRow, 0, replyControl);

    addReply.setVisible(false);
  }

  private void setupReplyArea() {
    replyArea = new PromptTextArea(messages.replyMessageBoxPrompt());
    replyArea.addStyleName("replyArea");
  }

  private void setupReplyControl() {
    replyControl = new HorizontalPanel();
    Button cancel = new Button(messages.cancelReply(), new ClickListener() {
      public void onClick(Widget widget) {
        hideReply();
      }
    });
    Button save = new Button(messages.saveReply(), new ClickListener() {
      public void onClick(Widget widget) {
        saveReply(replyArea.getText());
      }
    });

    replyControl.add(cancel);
    replyControl.add(save);

    replyControl.setCellHorizontalAlignment(cancel, HasHorizontalAlignment.ALIGN_RIGHT);
    replyControl.setCellWidth(cancel, "100%");
    replyControl.addStyleName("replyControl");
  }

  private void hideReply() {
    content.remove(replyArea);
    content.getFlexCellFormatter().removeStyleName(replyAreaRow, 0, "replyAreaContainer");
    content.remove(replyControl);
    replyArea.setText("");
    addReply.setVisible(true);
  }

  private void saveReply(String content) {
    Reply reply = new Reply();
    reply.setContent(content);
    reply.setParentMessage(comment);
    // todo: display loading

    ((CodeCommentRPCAsync) CoreGWT.getRPC("CodeCommentRPCAsync"))
        .addReply(comment, reply, new AsyncCallback() {
          public void onFailure(Throwable throwable) {
            // todo: catch error
          }

          public void onSuccess(Object object) {
            // todo: show message
            // todo: stop loading
            Reply reply = (Reply) object;
            comment.addReply(reply);

            // refresh comment display
            area.removeComment(comment);
            area.addComment(comment);

            // show replies
            if (!showingReplies) {
              toggleReplyDisplay();
            }

            // update replies display
            Widget rep = createReplyWidget(reply);
            replyPanel.add(rep);
            replyContainer.ensureVisible(rep);
            refreshReplyToggle();

            // hide reply
            hideReply();
          }
        });
  }

  private Widget createReplyWidget(Reply reply) {
    VerticalPanel replyContainer = new VerticalPanel();
    Label replyHeader =
        new Label(messages.replyHeader(reply.getAuthor().getUsername(),
            CoreGWT.getLocaleDateFormat().format(reply.getCreationDate())));
    Label replyContent = new Label(reply.getContent());

    replyContainer.add(replyHeader);
    replyContainer.add(replyContent);

    replyHeader.addStyleName("replyHeader");
    replyContent.addStyleName("replyContent");
    replyContainer.addStyleName("replyContainer");
    if (replyPanel.getWidgetCount() == 0) {
      replyContainer.addStyleName("firstReply");
    }
    return replyContainer;
  }

  private void refreshReplyToggle() {
    int replyCount = comment.getReplies().size();
    if (replyCount == 0) {
      replyToggle.setHTML(messages.noReplies());
      replyToggle.removeStyleName("link");
      return;
    }

    if (showingReplies) {
      replyToggle.setHTML(replyCount > 1 ? messages.hideReplies() : messages.hideReply());
    } else {
      replyToggle.setHTML(replyCount > 1 ? messages.showReplies(replyCount) : messages.showReply());
    }
    replyToggle.addStyleName("link");
  }

  private void toggleReplyDisplay() {
    if (replyContainer == null) {
      setupReplyContainer();
    }

    if (showingReplies) {
      content.remove(replyContainer);
    } else {
      content.setWidget(replyDisplayRow, 0, replyContainer);
    }

    showingReplies = !showingReplies;

    refreshReplyToggle();
  }

  private void setupReplyContainer() {
    Set replies = comment.getReplies();

    replyPanel = new VerticalPanel();
    replyContainer = new ScrollPanel(replyPanel);
    replyContainer.addStyleName("replies");

    Iterator it = replies.iterator();
    while (it.hasNext()) {
      replyPanel.add(createReplyWidget((Reply) it.next()));
    }
  }

// -------------------------- OTHER METHODS --------------------------

  protected boolean isEdited() {
    return (replyArea != null && !replyArea.showsPrompt() && !replyArea.getText().equals(""));
  }
}